package com.eva.androidlibrary.image;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.net.Uri;
import android.text.TextUtils;

import com.eva.androidlibrary.utils.DevUtil;
import com.eva.androidlibrary.utils.FileUtil;

import org.apache.http.client.ClientProtocolException;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * 
 * 基础图片工具类
 * 
 * 
 */
public class ImageHelper {
    private Context            mContext;
    private static ImageHelper _instance        = null;
    public static int          COMPRESS_QUALITY = 100;  // 图片质量

    public synchronized static ImageHelper getInstance(Context context) {

        if (_instance == null) {
            _instance = new ImageHelper(context);
        }
        return _instance;
    }

    private ImageHelper(Context context) {
        mContext = context;
    }

    /**
     * 载入图片
     * 
     * @param filePath
     *            图片路径
     * @return
     */
    public Bitmap getImage(String filePath) {
        return BitmapFactory.decodeFile(filePath);
    }

    /**
     * 指定最大宽度和最大高度下载入图片
     * @param filePath
     * @param maxWidth
     * @param maxHeight
     * @return
     */
    public Bitmap getImageWithBounds(String filePath, int maxWidth, int maxHeight) {
        return decodeFileWithBounds(filePath, maxWidth, maxHeight);
    }

    private Bitmap decodeFileWithBounds(String filePath, int maxWidth, int maxHeight) {
        Bitmap ret = null;
        if (maxWidth == 0 && maxHeight == 0) {// 都不限制？
            ret = BitmapFactory.decodeFile(filePath);
        } else {
            if (maxWidth == 0) {
                maxWidth = -1;
            }
            if (maxHeight == 0) {
                maxHeight = -1;
            }

            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(filePath, opts);
            if (opts.outWidth == -1) {// error
                return null;
            }
            int width = opts.outWidth;// 图片宽
            int height = opts.outHeight;// 图片高
            if (maxWidth >= width && maxHeight >= height) {// 略缩图比原图还大？！！
                ret = BitmapFactory.decodeFile(filePath);
            } else {
                // 计算到maxWidth的压缩比
                float inSampleSizeWidthFloat = (float) width / (float) maxWidth;
                int inSampleSizeWidth = Math.round(inSampleSizeWidthFloat);
                // 计算到maxHeight的压缩比
                float inSampleSizeHeightFloat = (float) height / (float) maxHeight;
                int inSampleSizeHeight = Math.round(inSampleSizeHeightFloat);

                int inSampleSize = Math.max(inSampleSizeWidth, inSampleSizeHeight);

                opts.inJustDecodeBounds = false;
                opts.inSampleSize = inSampleSize;
                ret = BitmapFactory.decodeFile(filePath, opts);
            }
        }
        return ret;
    }

    /**
     * 加载图片 按比例缩放保证图片宽和高不大于width和height
     * 
     * @param filePathOrUrl
     *            网络图片url或者本地路径
     * @param width
     *            宽
     * @param height
     *            高
     */
    public Bitmap getImage(String filePathOrUrl, int width, int height) throws FileNotFoundException {
        return getImage(filePathOrUrl, width, height, false);
    }

    final static int Normal = 0;
    final static int InSample       = 1;
    final static int InSampleAndCut = 2;

    /**
     * 加载图片<br>
     * <br>
     * 重要：本方法中isNeedCut=true使用了sdk level8的特性。使用本方法，需要保证你的app中android:minSdkVersion大于等于8，否则在低版本中会异常
     * 
     * @param filePath
     *            网络图片url或者本地路径
     * @param width
     *            宽
     * @param height
     *            高
     * @param isNeedCut
     *            true:缩放后将大于指定width和height的裁剪掉 false:仅按比例缩放保证图片宽和高不大于width和height
     * @return
     * @throws FileNotFoundException
     */
    @TargetApi(8)
    public Bitmap getImage(String filePath, int width, int height, boolean isNeedCut) throws FileNotFoundException {
        if (TextUtils.isEmpty(filePath)) {
            return null;
        }

        Bitmap ret = null;

        int imageLoadType = Normal;
        if (width != 0 && height != 0) {
            if (isNeedCut) {
                imageLoadType = InSampleAndCut;
            } else {
                imageLoadType = InSample;
            }
        }

        switch (imageLoadType) {
        case Normal:
            ret = getImage(filePath);
            break;
        case InSample:
            DevUtil.v("Eva", String.format("getImage:InSample task:%s", this));
            ret = getInSampleBitmap(filePath, width, height);
            break;
        case InSampleAndCut:
            DevUtil.v("Eva", String.format("getImage:InSampleAndCut task:%s", this));
            ret = getInSampleAndCutBitmap(filePath, width, height);
            break;
        }

        return ret;
    }

    /**
     * 重要：本方法中使用了sdk level8的特性。使用本方法，需要保证你的app中android:minSdkVersion大于等于8，否则在低版本中会异常 获得按比例的略缩图，图片为指定大小（先缩放再裁剪）
     * 
     * @param filePath
     * @param cutWidth
     * @param cutHeight
     * @return
     */
    @TargetApi(8)
    private Bitmap getInSampleAndCutBitmap(String filePath, int cutWidth, int cutHeight) {

        if (!DevUtil.hasFroyo()) {// 小于2.2的系统无法使用
            return BitmapFactory.decodeByteArray(new byte[] {}, 0, 0);
        }

        Bitmap ret = null;
        if (cutWidth == 0 && cutHeight == 0) {// 都不限制？
            ret = BitmapFactory.decodeFile(filePath);
        } else {
            Bitmap bitmap = BitmapFactory.decodeFile(filePath);
            ret = ThumbnailUtils.extractThumbnail(bitmap, cutWidth, cutHeight);

            if (bitmap != null && !bitmap.isRecycled()) {
                bitmap.recycle();
                bitmap = null;
            }
        }

        return ret;
    }

    // http://stackoverflow.com/questions/2641726/decoding-bitmaps-in-android-with-the-right-size
    /**
     * 取得图片的略缩图<br>
     * <br>
     * 缓存算法：<br>
     * 以字符串maxWidth + maxHeight + filePatch拼接字符串Md5为缓存文件名<br>
     * 缓存在mContext.getCacheDir()目录中,下次取直接可取得缓存图片。<br>
     * 
     * @param filePath
     * @param maxWidth
     *            0表示不限
     * @param maxHeight
     *            0表示不限
     * @return
     * @throws FileNotFoundException
     */
    private Bitmap getInSampleBitmap(String filePath, int maxWidth, int maxHeight) throws FileNotFoundException {

        Bitmap ret = null;

        if (maxWidth == 0 && maxHeight == 0) {// 都不限制？
            ret = BitmapFactory.decodeFile(filePath);
        } else {
            if (maxWidth == 0) {
                maxWidth = -1;
            }
            if (maxHeight == 0) {
                maxHeight = -1;
            }

            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(filePath, opts);
            if (opts.outWidth == -1) {// error
                return null;
            }
            int width = opts.outWidth;// 图片宽
            int height = opts.outHeight;// 图片高
            if (maxWidth >= width && maxHeight >= height) {// 略缩图比原图还大？！！
                DevUtil.v("zhutianjian**********", "decode without compress");
                ret = BitmapFactory.decodeFile(filePath);
            } else {
                // 计算到maxWidth的压缩比
                float inSampleSizeWidthFloat = (float) width / (float) maxWidth;
                int inSampleSizeWidth = Math.round(inSampleSizeWidthFloat);
                // 计算到maxHeight的压缩比
                float inSampleSizeHeightFloat = (float) height / (float) maxHeight;
                int inSampleSizeHeight = Math.round(inSampleSizeHeightFloat);

                int inSampleSize = Math.max(inSampleSizeWidth, inSampleSizeHeight);

                opts.inJustDecodeBounds = false;
                opts.inSampleSize = inSampleSize;
                ret = BitmapFactory.decodeFile(filePath, opts);
            }
        }

        return ret;
    }

    /**
     * 计算压缩比例大小
     * @param options
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    public int calculateInSampleSize(BitmapFactory.Options options,
                                            int reqWidth, int reqHeight) {
        // 源图片的高度和宽度
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;
        if (height > reqHeight || width > reqWidth) {
            // 计算出实际宽高和目标宽高的比率
            final int heightRatio = Math.round((float) height / (float) reqHeight);
            final int widthRatio = Math.round((float) width / (float) reqWidth);
            // 选择宽和高中最小的比率作为inSampleSize的值，这样可以保证最终图片的宽和高
            // 一定都会大于等于目标的宽和高。
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }
        return inSampleSize;
    }

    /**
     * 通过资源得到压缩到指定宽高的图片
     * @param res
     * @param resId
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    public Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
                                                         int reqWidth, int reqHeight) {
        // 第一次解析将inJustDecodeBounds设置为true，来获取图片大小
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);
        // 调用上面定义的方法计算inSampleSize值
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        // 使用获取到的inSampleSize值再次解析图片
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

    /**
     * 通过文件路径得到压缩到指定宽高的图片
     * @param path
     * @param reqWidth
     * @param reqHeight
     * @return
     */
    public Bitmap decodeSampledBitmapFromFile(String path, int reqWidth, int reqHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(path, options);
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(path, options);
    }

    /**
     * 将图片压缩到指定大小
     * @param image
     * @param imageSize 指定的图片大小，单位kb
     * @return
     */
    public Bitmap compressImage(Bitmap image, int imageSize) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(CompressFormat.JPEG, 100, baos);
        // 大于1M先压百分之五十
        if (baos.toByteArray().length / 1024 > 1024) {
            baos.reset();
            image.compress(CompressFormat.JPEG, 50, baos);
        }
        int options = 100;
        int length = baos.toByteArray().length / 1024;
        while ( length > imageSize) {  //循环判断如果压缩后图片是否大于传入的参数,大于继续压缩
            baos.reset();//重置baos即清空baos
            options -= 10;//每次都减少10
            image.compress(CompressFormat.JPEG, options, baos);//这里压缩options%，把压缩后的数据存放到baos中
            length = baos.toByteArray().length / 1024;
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中
        image = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片
        return image;
    }

    /**
     * Saves the bitmap by given directory, filename, and format; if the directory is given null, then saves it under the cache directory.
     */
    public static File saveBitmap(Context context, Bitmap bitmap, String directory, String filename,
                                  CompressFormat format) {

        if (TextUtils.isEmpty(directory)) {
            directory = FileUtil.getExternalStorageDirectory(context);
        }

        OutputStream os = null;
        filename = (format == CompressFormat.PNG) ? filename + ".png" : filename + ".jpg";
        File file = new File(directory, filename);
        file.mkdirs();
        if (file.exists()) {
            file.delete();
        }

        if (bitmap == null) {
            return file;
        }

        try {
            file.createNewFile();
            os = new FileOutputStream(file);
            bitmap.compress(format, COMPRESS_QUALITY, os);
            os.flush();
            Uri data = Uri.parse("file:///" + file.getAbsolutePath());
            context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));

            DevUtil.v("Eva", "saveBitmap:" + file.length() / 1024 + "KB");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeStream(os);
        }
        return file;
    }

    private static void closeStream(Closeable stream) {
        if (stream != null) {
            try {
                stream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
